//! # 网关认证中间件
//!
//! 将 rustcloud-security 的认证功能集成到网关请求处理流程中。

use async_trait::async_trait;
use std::sync::Arc;

use rustcloud_security::prelude::*;
use crate::core::{
    Middleware, GatewayRequest, GatewayResponse, GatewayContext, GatewayResult, GatewayError, AuthInfo
};
use crate::auth::{GatewayAuthConfig, AuthProviderConfig, PermissionConfig, CacheConfig};

/// 网关认证中间件
pub struct GatewayAuthMiddleware {
    config: GatewayAuthConfig,
    auth_provider: Arc<dyn AuthProvider>,
    permission_checker: Arc<dyn PermissionChecker>,
}

impl GatewayAuthMiddleware {
    /// 创建新的认证中间件
    pub fn new(
        config: GatewayAuthConfig,
        auth_provider: Arc<dyn AuthProvider>,
        permission_checker: Arc<dyn PermissionChecker>,
    ) -> Self {
        Self {
            config,
            auth_provider,
            permission_checker,
        }
    }

    /// 创建默认配置的认证中间件
    pub async fn with_default_config(
        auth_provider: Arc<dyn AuthProvider>,
    ) -> GatewayResult<Self> {
        let config = GatewayAuthConfig::default();
        let permission_checker = Arc::new(DefaultPermissionChecker::new());
        
        Ok(Self::new(config, auth_provider, permission_checker))
    }

    /// 检查路径是否在白名单中
    fn is_public_path(&self, path: &str) -> bool {
        for public_path in &self.config.permission_config.public_paths {
            if path.starts_with(public_path) {
                return true;
            }
        }
        false
    }

    /// 从网关请求创建认证上下文
    fn create_auth_context(&self, request: &GatewayRequest) -> AuthContext {
        AuthContext::new(
            request.headers.clone(),
            request.method.clone(),
            request.uri.clone(),
            request.remote_addr.clone(),
        )
        .with_query_params(request.query_params.clone())
    }

    /// 从网关请求创建资源
    fn create_resource(&self, request: &GatewayRequest) -> Resource {
        Resource::new(
            request.service.clone().unwrap_or_else(|| "unknown".to_string()),
            request.path.clone(),
            request.method.clone(),
        )
    }

    /// 执行认证
    async fn authenticate_request(&self, context: &AuthContext) -> GatewayResult<Principal> {
        self.auth_provider
            .authenticate(context)
            .await
            .map_err(|e| self.convert_auth_error(e))
    }

    /// 执行授权检查
    async fn authorize_request(
        &self,
        principal: &Principal,
        resource: &Resource,
    ) -> GatewayResult<bool> {
        if !self.config.permission_config.enabled {
            return Ok(true);
        }

        self.permission_checker
            .check_resource(principal, resource)
            .await
            .map_err(|e| self.convert_auth_error(e))
    }

    /// 转换认证错误为网关错误
    fn convert_auth_error(&self, auth_error: AuthError) -> GatewayError {
        match auth_error {
            AuthError::AuthenticationFailed(msg) => GatewayError::authentication(msg),
            AuthError::AuthorizationFailed(msg) => GatewayError::authorization(msg),
            AuthError::InvalidToken(msg) => GatewayError::authentication(format!("Invalid token: {}", msg)),
            AuthError::TokenExpired => GatewayError::authentication("Token expired"),
            AuthError::InsufficientPermissions { required, current: _ } => {
                GatewayError::authorization(format!("Insufficient permissions, required: {}", required))
            }
            AuthError::UnknownUser(msg) => GatewayError::authentication(format!("Unknown user: {}", msg)),
            AuthError::InvalidCredentials(msg) => GatewayError::authentication(format!("Invalid credentials: {}", msg)),
            AuthError::AccountLocked(msg) => GatewayError::authorization(format!("Account locked: {}", msg)),
            AuthError::AccountDisabled(msg) => GatewayError::authorization(format!("Account disabled: {}", msg)),
            AuthError::SessionExpired => GatewayError::authentication("Session expired"),
            AuthError::InvalidSession(msg) => GatewayError::authentication(format!("Invalid session: {}", msg)),
            AuthError::RateLimitExceeded(msg) => GatewayError::too_many_requests(format!("Rate limit exceeded: {}", msg)),
            AuthError::IpBlocked(msg) => GatewayError::authorization(format!("IP blocked: {}", msg)),
            _ => GatewayError::internal(auth_error.to_string()),
        }
    }
}

#[async_trait]
impl Middleware for GatewayAuthMiddleware {
    async fn before_request(
        &self,
        request: GatewayRequest,
        context: Arc<GatewayContext>,
    ) -> GatewayResult<GatewayRequest> {
        // 如果认证未启用，直接通过
        if !self.config.enabled {
            return Ok(request);
        }

        // 检查是否为公开路径
        if self.is_public_path(&request.path) {
            return Ok(request);
        }

        // 创建认证上下文
        let auth_context = self.create_auth_context(&request);

        // 执行认证
        let principal = self.authenticate_request(&auth_context).await?;

        // 检查主体是否过期
        if principal.is_expired() {
            return Err(GatewayError::authentication("Principal expired"));
        }

        // 创建资源
        let resource = self.create_resource(&request);

        // 执行授权检查
        let authorized = self.authorize_request(&principal, &resource).await?;
        if !authorized {
            return Err(GatewayError::authorization("Access denied"));
        }

        // 将认证信息添加到上下文
        let auth_info = AuthInfo {
            user_id: principal.id.clone(),
            username: Some(principal.name.clone()),
            roles: principal.get_roles().iter().map(|s| s.to_string()).collect(),
            permissions: principal.get_permissions().iter().map(|s| s.to_string()).collect(),
            auth_type: self.auth_provider.provider_name().to_string(),
            attributes: principal.attributes.clone(),
        };
        
        // 由于 context 是 Arc，我们需要使用不同的方式来传递认证信息
        // 这里我们可以在request中添加自定义头部或者使用其他方式

        Ok(request)
    }

    async fn after_response(
        &self,
        response: GatewayResponse,
        _context: Arc<GatewayContext>,
    ) -> GatewayResult<GatewayResponse> {
        // 认证中间件在响应后处理阶段暂无特殊逻辑
        Ok(response)
    }

    fn name(&self) -> &str {
        "gateway_auth_middleware"
    }

    fn priority(&self) -> i32 {
        10 // 较高优先级，在大部分中间件之前执行
    }
}

// 导入新的统一权限检查器
use rustcloud_core::auth::PermissionCheckerFactory;

// 使用新的统一权限检查器替代本地实现
// 原有的 DefaultPermissionChecker 已被移除，现在使用 rustcloud-core 中的实现
pub type DefaultPermissionChecker = rustcloud_core::auth::CommonPermissionChecker;

impl Default for GatewayAuthConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            provider_config: AuthProviderConfig {
                default_provider: "jwt".to_string(),
                enabled_providers: vec!["jwt".to_string(), "api_key".to_string()],
            },
            permission_config: PermissionConfig {
                enabled: true,
                default_deny: true,
                public_paths: vec![
                    "/health".to_string(),
                    "/metrics".to_string(),
                    "/ping".to_string(),
                    "/api/public".to_string(),
                ],
            },
            cache_config: Some(CacheConfig {
                ttl_seconds: 300,
                max_entries: 10000,
            }),
        }
    }
}

impl Default for PermissionConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            default_deny: true,
            public_paths: vec![
                "/health".to_string(),
                "/metrics".to_string(),
                "/ping".to_string(),
            ],
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;
    use axum::http::HeaderMap;

    struct MockAuthProvider {
        should_authenticate: bool,
        should_error: bool,
    }

    impl MockAuthProvider {
        fn new(should_authenticate: bool, should_error: bool) -> Self {
            Self {
                should_authenticate,
                should_error,
            }
        }
    }

    #[async_trait]
    impl AuthProvider for MockAuthProvider {
        async fn authenticate(&self, _context: &AuthContext) -> AuthResult<Principal> {
            if self.should_error {
                return Err(AuthError::InternalError("Mock error".to_string()));
            }

            if !self.should_authenticate {
                return Err(AuthError::AuthenticationFailed("Mock auth failed".to_string()));
            }

            Ok(Principal::new(
                "test_user".to_string(),
                "Test User".to_string(),
                PrincipalType::User,
            ))
        }

        async fn validate_token(&self, _token: &str) -> AuthResult<bool> {
            Ok(self.should_authenticate)
        }

        fn provider_type(&self) -> AuthProviderType {
            AuthProviderType::Custom("mock".to_string())
        }

        fn provider_name(&self) -> &str {
            "mock_provider"
        }
    }

    #[tokio::test]
    async fn test_gateway_auth_middleware_success() {
        let auth_provider = Arc::new(MockAuthProvider::new(true, false));
        let middleware = GatewayAuthMiddleware::with_default_config(auth_provider)
            .await
            .unwrap();

        let mut headers = HeaderMap::new();
        headers.insert("authorization", "Bearer test_token".parse().unwrap());

        let request = GatewayRequest {
            id: "test_request".to_string(),
            method: "GET".to_string(),
            uri: "/api/test".to_string(),
            path: "/api/test".to_string(),
            query_params: HashMap::new(),
            headers,
            body: None,
            remote_addr: Some("127.0.0.1".to_string()),
            service: Some("test-service".to_string()),
            timestamp: std::time::SystemTime::now(),
        };

        let mut context = GatewayContext::new();
        let result = middleware.handle(request, &mut context).await;

        assert!(result.is_ok());
        assert!(context.auth_info().is_some());
        assert!(context.auth_info().unwrap().authenticated);
    }

    #[tokio::test]
    async fn test_gateway_auth_middleware_public_path() {
        let auth_provider = Arc::new(MockAuthProvider::new(false, false));
        let middleware = GatewayAuthMiddleware::with_default_config(auth_provider)
            .await
            .unwrap();

        let request = GatewayRequest {
            id: "test_request".to_string(),
            method: "GET".to_string(),
            uri: "/health".to_string(),
            path: "/health".to_string(),
            query_params: HashMap::new(),
            headers: HeaderMap::new(),
            body: None,
            remote_addr: Some("127.0.0.1".to_string()),
            service: Some("test-service".to_string()),
            timestamp: std::time::SystemTime::now(),
        };

        let mut context = GatewayContext::new();
        let result = middleware.handle(request, &mut context).await;

        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_gateway_auth_middleware_auth_failure() {
        let auth_provider = Arc::new(MockAuthProvider::new(false, false));
        let middleware = GatewayAuthMiddleware::with_default_config(auth_provider)
            .await
            .unwrap();

        let request = GatewayRequest {
            id: "test_request".to_string(),
            method: "GET".to_string(),
            uri: "/api/protected".to_string(),
            path: "/api/protected".to_string(),
            query_params: HashMap::new(),
            headers: HeaderMap::new(),
            body: None,
            remote_addr: Some("127.0.0.1".to_string()),
            service: Some("test-service".to_string()),
            timestamp: std::time::SystemTime::now(),
        };

        let mut context = GatewayContext::new();
        let result = middleware.handle(request, &mut context).await;

        assert!(result.is_err());
    }

    #[test]
    fn test_default_permission_checker() {
        let checker = DefaultPermissionChecker::new();
        
        let admin_principal = Principal::new(
            "admin".to_string(),
            "Admin User".to_string(),
            PrincipalType::Admin,
        );

        let user_principal = Principal::new(
            "user".to_string(),
            "Regular User".to_string(),
            PrincipalType::User,
        ).add_permission("read:posts".to_string());

        // 管理员应该有所有权限
        assert!(checker.check_permission_simple(&admin_principal, "any:permission"));

        // 普通用户应该只有特定权限
        assert!(checker.check_permission_simple(&user_principal, "read:posts"));
        assert!(!checker.check_permission_simple(&user_principal, "write:posts"));
    }
}